﻿using System;
using System.Data;
using System.Security.Cryptography;
using System.IO;
using System.Text;

namespace OpenQuarters.WebQuarters.Utils
{
    public class Encryption
    {
        public class RijndaelSimple
        {
            static string passPhrase = "Pas5pr@se";        // can be any string
            static string saltValue = "s@1tValue";        // can be any string
            static string hashAlgorithm = "SHA1";             // can be "MD5"
            static int passwordIterations = 2;                  // can be any number
            static string initVector = "@1B2c3D4e5F6g7H8"; // must be 16 bytes
            static int keySize = 128;                // can be 192 or 128

            public static string Encrypt(string data)
            {

                string cipherText = RijndaelSimple.Encrypt(data,
                                                    passPhrase,
                                                    saltValue,
                                                    hashAlgorithm,
                                                    passwordIterations,
                                                    initVector,
                                                    keySize);

                return cipherText;
            }

            public static string Decrypt(string data)
            {

                string cipherText = RijndaelSimple.Decrypt(data,
                                                    passPhrase,
                                                    saltValue,
                                                    hashAlgorithm,
                                                    passwordIterations,
                                                    initVector,
                                                    keySize);

                return cipherText;
            }

            /// <summary>
            /// Encrypts specified plaintext using Rijndael symmetric key algorithm
            /// and returns a base64-encoded result.
            /// </summary>
            /// <param name="plainText">
            /// Plaintext value to be encrypted.
            /// </param>
            /// <param name="passPhrase">
            /// Passphrase from which a pseudo-random password will be derived. The
            /// derived password will be used to generate the encryption key.
            /// Passphrase can be any string. In this example we assume that this
            /// passphrase is an ASCII string.
            /// </param>
            /// <param name="saltValue">
            /// Salt value used along with passphrase to generate password. Salt can
            /// be any string. In this example we assume that salt is an ASCII string.
            /// </param>
            /// <param name="hashAlgorithm">
            /// Hash algorithm used to generate password. Allowed values are: "MD5" and
            /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
            /// </param>
            /// <param name="passwordIterations">
            /// Number of iterations used to generate password. One or two iterations
            /// should be enough.
            /// </param>
            /// <param name="initVector">
            /// Initialization vector (or IV). This value is required to encrypt the
            /// first block of plaintext data. For RijndaelManaged class IV must be 
            /// exactly 16 ASCII characters long.
            /// </param>
            /// <param name="keySize">
            /// Size of encryption key in bits. Allowed values are: 128, 192, and 256. 
            /// Longer keys are more secure than shorter keys.
            /// </param>
            /// <returns>
            /// Encrypted value formatted as a base64-encoded string.
            /// </returns>
            public static string Encrypt(string plainText,
                                         string passPhrase,
                                         string saltValue,
                                         string hashAlgorithm,
                                         int passwordIterations,
                                         string initVector,
                                         int keySize)
            {
                // Convert strings into byte arrays.
                // Let us assume that strings only contain ASCII codes.
                // If strings include Unicode characters, use Unicode, UTF7, or UTF8 
                // encoding.
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
                byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

                // Convert our plaintext into a byte array.
                // Let us assume that plaintext contains UTF8-encoded characters.
                byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

                // First, we must create a password, from which the key will be derived.
                // This password will be generated from the specified passphrase and 
                // salt value. The password will be created using the specified hash 
                // algorithm. Password creation can be done in several iterations.
                PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                                passPhrase,
                                                                saltValueBytes,
                                                                hashAlgorithm,
                                                                passwordIterations);

                // Use the password to generate pseudo-random bytes for the encryption
                // key. Specify the size of the key in bytes (instead of bits).
                byte[] keyBytes = password.GetBytes(keySize / 8);

                // Create uninitialized Rijndael encryption object.
                RijndaelManaged symmetricKey = new RijndaelManaged();

                // It is reasonable to set encryption mode to Cipher Block Chaining
                // (CBC). Use default options for other symmetric key parameters.
                symmetricKey.Mode = CipherMode.CBC;

                // Generate encryptor from the existing key bytes and initialization 
                // vector. Key size will be defined based on the number of the key 
                // bytes.
                ICryptoTransform encryptor = symmetricKey.CreateEncryptor(
                                                                 keyBytes,
                                                                 initVectorBytes);

                // Define memory stream which will be used to hold encrypted data.
                MemoryStream memoryStream = new MemoryStream();

                // Define cryptographic stream (always use Write mode for encryption).
                CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                             encryptor,
                                                             CryptoStreamMode.Write);
                // Start encrypting.
                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);

                // Finish encrypting.
                cryptoStream.FlushFinalBlock();

                // Convert our encrypted data from a memory stream into a byte array.
                byte[] cipherTextBytes = memoryStream.ToArray();

                // Close both streams.
                memoryStream.Close();
                cryptoStream.Close();

                // Convert encrypted data into a base64-encoded string.
                string cipherText = Convert.ToBase64String(cipherTextBytes);

                // Return encrypted string.
                return cipherText;
            }

            /// <summary>
            /// Decrypts specified ciphertext using Rijndael symmetric key algorithm.
            /// </summary>
            /// <param name="cipherText">
            /// Base64-formatted ciphertext value.
            /// </param>
            /// <param name="passPhrase">
            /// Passphrase from which a pseudo-random password will be derived. The
            /// derived password will be used to generate the encryption key.
            /// Passphrase can be any string. In this example we assume that this
            /// passphrase is an ASCII string.
            /// </param>
            /// <param name="saltValue">
            /// Salt value used along with passphrase to generate password. Salt can
            /// be any string. In this example we assume that salt is an ASCII string.
            /// </param>
            /// <param name="hashAlgorithm">
            /// Hash algorithm used to generate password. Allowed values are: "MD5" and
            /// "SHA1". SHA1 hashes are a bit slower, but more secure than MD5 hashes.
            /// </param>
            /// <param name="passwordIterations">
            /// Number of iterations used to generate password. One or two iterations
            /// should be enough.
            /// </param>
            /// <param name="initVector">
            /// Initialization vector (or IV). This value is required to encrypt the
            /// first block of plaintext data. For RijndaelManaged class IV must be
            /// exactly 16 ASCII characters long.
            /// </param>
            /// <param name="keySize">
            /// Size of encryption key in bits. Allowed values are: 128, 192, and 256.
            /// Longer keys are more secure than shorter keys.
            /// </param>
            /// <returns>
            /// Decrypted string value.
            /// </returns>
            /// <remarks>
            /// Most of the logic in this function is similar to the Encrypt
            /// logic. In order for decryption to work, all parameters of this function
            /// - except cipherText value - must match the corresponding parameters of
            /// the Encrypt function which was called to generate the
            /// ciphertext.
            /// </remarks>
            public static string Decrypt(string cipherText,
                                         string passPhrase,
                                         string saltValue,
                                         string hashAlgorithm,
                                         int passwordIterations,
                                         string initVector,
                                         int keySize)
            {
                // Convert strings defining encryption key characteristics into byte
                // arrays. Let us assume that strings only contain ASCII codes.
                // If strings include Unicode characters, use Unicode, UTF7, or UTF8
                // encoding.
                byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
                byte[] saltValueBytes = Encoding.ASCII.GetBytes(saltValue);

                // Convert our ciphertext into a byte array.
                byte[] cipherTextBytes = Convert.FromBase64String(cipherText);

                // First, we must create a password, from which the key will be 
                // derived. This password will be generated from the specified 
                // passphrase and salt value. The password will be created using
                // the specified hash algorithm. Password creation can be done in
                // several iterations.
                PasswordDeriveBytes password = new PasswordDeriveBytes(
                                                                passPhrase,
                                                                saltValueBytes,
                                                                hashAlgorithm,
                                                                passwordIterations);

                // Use the password to generate pseudo-random bytes for the encryption
                // key. Specify the size of the key in bytes (instead of bits).
                byte[] keyBytes = password.GetBytes(keySize / 8);

                // Create uninitialized Rijndael encryption object.
                RijndaelManaged symmetricKey = new RijndaelManaged();

                // It is reasonable to set encryption mode to Cipher Block Chaining
                // (CBC). Use default options for other symmetric key parameters.
                symmetricKey.Mode = CipherMode.CBC;

                // Generate decryptor from the existing key bytes and initialization 
                // vector. Key size will be defined based on the number of the key 
                // bytes.
                ICryptoTransform decryptor = symmetricKey.CreateDecryptor(
                                                                 keyBytes,
                                                                 initVectorBytes);

                // Define memory stream which will be used to hold encrypted data.
                MemoryStream memoryStream = new MemoryStream(cipherTextBytes);

                // Define cryptographic stream (always use Read mode for encryption).
                CryptoStream cryptoStream = new CryptoStream(memoryStream,
                                                              decryptor,
                                                              CryptoStreamMode.Read);

                // Since at this point we don't know what the size of decrypted data
                // will be, allocate the buffer long enough to hold ciphertext;
                // plaintext is never longer than ciphertext.
                byte[] plainTextBytes = new byte[cipherTextBytes.Length];

                // Start decrypting.
                int decryptedByteCount = cryptoStream.Read(plainTextBytes,
                                                           0,
                                                           plainTextBytes.Length);

                // Close both streams.
                memoryStream.Close();
                cryptoStream.Close();

                // Convert decrypted data into a string. 
                // Let us assume that the original plaintext string was UTF8-encoded.
                string plainText = Encoding.UTF8.GetString(plainTextBytes,
                                                           0,
                                                           decryptedByteCount);

                // Return decrypted string.   
                return plainText;
            }
        }

//        Public Class Encryption64
//    Private key() As Byte = {}
//    Private IV() As Byte = {&H12, &H34, &H56, &H78, &H90, &HAB, &HCD, &HEF}

//    Public Function Decrypt(ByVal stringToDecrypt As String, _
//        ByVal sEncryptionKey As String) As String
//        Dim inputByteArray(stringToDecrypt.Length) As Byte
//         Try
//            key = System.Text.Encoding.UTF8.GetBytes(Left(sEncryptionKey, 8))
//            Dim des As New DESCryptoServiceProvider()
//            inputByteArray = Convert.FromBase64String(stringToDecrypt)
//            Dim ms As New MemoryStream()
//            Dim cs As New CryptoStream(ms, des.CreateDecryptor(key, IV), _
//                CryptoStreamMode.Write)
//            cs.Write(inputByteArray, 0, inputByteArray.Length)
//            cs.FlushFinalBlock()
//            Dim encoding As System.Text.Encoding = System.Text.Encoding.UTF8
//            Return encoding.GetString(ms.ToArray())
//        Catch e As Exception
//            Return e.Message
//        End Try
//    End Function

//    Public Function Encrypt(ByVal stringToEncrypt As String, _
//        ByVal SEncryptionKey As String) As String
//        Try
//            key = System.Text.Encoding.UTF8.GetBytes(Left(SEncryptionKey, 8))
//            Dim des As New DESCryptoServiceProvider()
//            Dim inputByteArray() As Byte = Encoding.UTF8.GetBytes( _
//                stringToEncrypt)
//            Dim ms As New MemoryStream()
//            Dim cs As New CryptoStream(ms, des.CreateEncryptor(key, IV), _
//                CryptoStreamMode.Write)
//            cs.Write(inputByteArray, 0, inputByteArray.Length)
//            cs.FlushFinalBlock()
//            Return Convert.ToBase64String(ms.ToArray())
//        Catch e As Exception
//            Return e.Message
//        End Try
//    End Function

//End Class

        public class SimpleDES
        {
            // Change these keys
            private static byte[] Vector = { 217, 19, 11, 24, 26, 85, 45, 231 };
            private static string encryptionKey = "SK$AkK!#";

            public static string Encrypt(String data)
            {
                byte[] key = Encoding.UTF8.GetBytes(encryptionKey.PadLeft(8));

                DES des = new DESCryptoServiceProvider();
                byte[] inputArray = Encoding.UTF8.GetBytes(data);
                MemoryStream sout = new MemoryStream();
                CryptoStream encStream = new CryptoStream(sout, des.CreateEncryptor(key, Vector), CryptoStreamMode.Write);
                // Console.WriteLine("Encrypting...");
                encStream.Write(inputArray, 0, inputArray.Length);
                encStream.FlushFinalBlock();

                return Convert.ToBase64String(sout.ToArray());
            }

            public static string Decrypt(String data)
            {
                byte[] inputArray = new byte[data.Length];
                byte[] key = Encoding.UTF8.GetBytes(encryptionKey.PadLeft(8));

                DES des = new DESCryptoServiceProvider();

                inputArray = Convert.FromBase64String(data);
                MemoryStream sout = new MemoryStream();
                CryptoStream encStream = new CryptoStream(sout, des.CreateEncryptor(key, Vector), CryptoStreamMode.Write);
                // Console.WriteLine("Encrypting...");
                encStream.Write(inputArray, 0, inputArray.Length);
                encStream.FlushFinalBlock();

                System.Text.Encoding encoding = System.Text.Encoding.UTF8;
                return encoding.GetString(sout.ToArray());
            }



            //    public static string Encrypt(string value)
            //    {
            //        DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            //        des.GenerateKey();
            //        byte[] key = des.Key; // save this!

            //        ICryptoTransform encryptor = des.CreateEncryptor();
            //        // encrypt
            //        byte[] enc = encryptor.TransformFinalBlock(new byte[] { 1, 2, 3, 4 }, 0, 4);

            //        return enc.ToString();
            //    }

            //    public static string Decrypt(string encodedValue)
            //    {
            //        DESCryptoServiceProvider des = new DESCryptoServiceProvider();
            //        des.GenerateKey();
            //        byte[] key = des.Key; // save this!

            //        ICryptoTransform decryptor = des.CreateDecryptor();

            //        // decrypt
            //        byte[] originalAgain = decryptor.TransformFinalBlock(enc, 0, enc.Length);
            //        return originalAgain.ToString();
            //    }
        }

        public class SimpleAES
        {
            // Change these keys
            private byte[] Key = { 123, 217, 19, 11, 24, 26, 85, 45, 112, 184, 27, 162, 37, 112, 231, 209, 241, 24, 175, 123, 173, 53, 142, 29, 24, 26, 17, 218, 131, 236, 53, 209 };
            private byte[] Vector = { 146, 64, 23, 111, 23, 3, 76, 119, 231, 121, 221, 112, 2, 32, 114, 17 };

            private ICryptoTransform EncryptorTransform, DecryptorTransform;
            private System.Text.UTF8Encoding UTFEncoder;

            public SimpleAES()
            {
                //This is our encryption method
                RijndaelManaged rm = new RijndaelManaged();

                //Create an encryptor and a decryptor using our encryption method, key, and vector.
                EncryptorTransform = rm.CreateEncryptor(this.Key, this.Vector);
                DecryptorTransform = rm.CreateDecryptor(this.Key, this.Vector);

                //Used to translate bytes to text and vice versa
                UTFEncoder = new System.Text.UTF8Encoding();
            }

            /// -------------- Two Utility Methods (not used but may be useful) -----------
            /// Generates an encryption key.
            static public byte[] GenerateEncryptionKey()
            {
                //Generate a Key.
                RijndaelManaged rm = new RijndaelManaged();
                rm.GenerateKey();
                return rm.Key;
            }

            /// Generates a unique encryption vector
            static public byte[] GenerateEncryptionVector()
            {
                //Generate a Vector
                RijndaelManaged rm = new RijndaelManaged();
                rm.GenerateIV();
                return rm.IV;
            }


            /// ----------- The commonly used methods ------------------------------    
            /// Encrypt some text and return a string suitable for passing in a URL.
            public string EncryptToString(string TextValue)
            {
                return ByteArrToString(Encrypt(TextValue));
            }

            /// Encrypt some text and return an encrypted byte array.
            public byte[] Encrypt(string TextValue)
            {
                //Translates our text value into a byte array.
                Byte[] bytes = UTFEncoder.GetBytes(TextValue);

                //Used to stream the data in and out of the CryptoStream.
                MemoryStream memoryStream = new MemoryStream();

                /*
                 * We will have to write the unencrypted bytes to the stream,
                 * then read the encrypted result back from the stream.
                 */
                #region Write the decrypted value to the encryption stream
                CryptoStream cs = new CryptoStream(memoryStream, EncryptorTransform, CryptoStreamMode.Write);
                cs.Write(bytes, 0, bytes.Length);
                cs.FlushFinalBlock();
                #endregion

                #region Read encrypted value back out of the stream
                memoryStream.Position = 0;
                byte[] encrypted = new byte[memoryStream.Length];
                memoryStream.Read(encrypted, 0, encrypted.Length);
                #endregion

                //Clean up.
                cs.Close();
                memoryStream.Close();

                return encrypted;
            }

            /// The other side: Decryption methods
            public string DecryptString(string EncryptedString)
            {
                return Decrypt(StrToByteArray(EncryptedString));
            }

            /// Decryption when working with byte arrays.    
            public string Decrypt(byte[] EncryptedValue)
            {
                #region Write the encrypted value to the decryption stream
                MemoryStream encryptedStream = new MemoryStream();
                CryptoStream decryptStream = new CryptoStream(encryptedStream, DecryptorTransform, CryptoStreamMode.Write);
                decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
                decryptStream.FlushFinalBlock();
                #endregion

                #region Read the decrypted value from the stream.
                encryptedStream.Position = 0;
                Byte[] decryptedBytes = new Byte[encryptedStream.Length];
                encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
                encryptedStream.Close();
                #endregion
                return UTFEncoder.GetString(decryptedBytes);
            }

            /// Convert a string to a byte array.  NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
            //      System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            //      return encoding.GetBytes(str);
            // However, this results in character values that cannot be passed in a URL.  So, instead, I just
            // lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
            public byte[] StrToByteArray(string str)
            {
                if (str.Length == 0)
                    throw new Exception("Invalid string value in StrToByteArray");

                byte val;
                byte[] byteArr = new byte[str.Length / 3];
                int i = 0;
                int j = 0;
                do
                {
                    val = byte.Parse(str.Substring(i, 3));
                    byteArr[j++] = val;
                    i += 3;
                }
                while (i < str.Length);
                return byteArr;
            }

            // Same comment as above.  Normally the conversion would use an ASCII encoding in the other direction:
            //      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
            //      return enc.GetString(byteArr);    
            public string ByteArrToString(byte[] byteArr)
            {
                byte val;
                string tempStr = "";
                for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
                {
                    val = byteArr[i];
                    if (val < (byte)10)
                        tempStr += "00" + val.ToString();
                    else if (val < (byte)100)
                        tempStr += "0" + val.ToString();
                    else
                        tempStr += val.ToString();
                }
                return tempStr;
            }
        }
    }
}
